package cc.shacocloud.redis.study.lock.example;

import cc.shacocloud.redis.study.lock.MutexLock;
import cc.shacocloud.redis.study.lock.MutexLockMo;
import cc.shacocloud.redis.study.lock.impl.RedisMutexLock;
import cc.shacocloud.redis.study.util.redis.RedisUtil;
import cn.hutool.core.date.DateUtil;
import cc.shacocloud.redis.study.lock.OptimisticLock;
import cc.shacocloud.redis.study.lock.TokenLimiterLock;
import cc.shacocloud.redis.study.lock.impl.RedisOptimisticLock;
import cc.shacocloud.redis.study.lock.impl.RedisTokenLimiterLock;
import lombok.extern.slf4j.Slf4j;

import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Consumer;

/**
 * 简单示例
 */
@Slf4j
public class Simple {

    public static void main(String[] args) throws InterruptedException {
        RedisUtil.initAndPing();

        mutexLock();
        tokenLimiterLock();
        optimisticLock();
    }

    /**
     * 乐观锁
     */
    private static void optimisticLock() {
        System.out.println("\n----------------- 乐观锁 ----------------------------");
        String lockName = "shacocloud-optimistic-lock";

        OptimisticLock lock = new RedisOptimisticLock();

        int version = lock.getLockVersion(lockName);
//        int version1 = lock.getLockVersion(lockName);

        boolean submitLock = lock.submitLock(lockName, version);
        if (submitLock) {
            System.out.println("提交成功！");
        } else {
            System.out.println("提交失败！");
        }
    }

    /**
     * 令牌桶锁
     */
    private static void tokenLimiterLock() throws InterruptedException {
        System.out.println("\n----------------- 令牌桶锁 ----------------------------");

        String lockName = "shacocloud-token-limiter-lock";

        // 令牌数量限制  线程池大小  并发任务数量
        int limiter = 3, poolSize = 4, concurrency = 8;
        TokenLimiterLock limiterLock = new RedisTokenLimiterLock(limiter, lockName);

        ExecutorService executor = Executors.newFixedThreadPool(poolSize);

        // 计数锁 仅仅用于统计关闭线程池
        CountDownLatch latch = new CountDownLatch(concurrency);

        for (int i = 0; i < concurrency; i++) {
            executor.execute(() -> {
                AtomicReference<Consumer<Void>> reference = new AtomicReference<>();

                Consumer<Void> consumer = (v) -> {
                    boolean acquire = limiterLock.acquire();
                    if (acquire) {
                        // 获取锁成功 打印日志
                        log.info("获取锁成功....");
                        // 睡眠1秒 将锁释放
                        try {
                            Thread.sleep(1000);
                        } catch (InterruptedException ignore) {
                        }
                        limiterLock.release();

                        latch.countDown();
                    } else {
                        // 获取锁失败 打印日志 睡眠1秒 递归尝试
                        log.info("获取锁失败....");

                        try {
                            Thread.sleep(1000);
                        } catch (InterruptedException ignore) {
                        }

                        reference.get().accept(null);
                    }
                };

                reference.set(consumer);
                consumer.accept(null);
            });
        }

        // 全部获取成功则关闭线程池
        latch.await();
        executor.shutdown();
    }

    /**
     * 互斥锁
     */
    private static void mutexLock() throws InterruptedException {
        System.out.println("\n----------------- 互斥锁 ----------------------------");
        MutexLock mutexLock = new RedisMutexLock();

        String lockName = "shacocloud-mutex-lock";
        int sNum = 0;

        while (true) {
            MutexLockMo lockMo = mutexLock.tryLock(lockName, DateUtil.offsetSecond(DateUtil.date(), 5));

            if (lockMo.isStatus()) {
                System.out.println(" '" + lockMo.getLockName() + "' 获取锁成功，" +
                        "凭证： '" + lockMo.getReentrantCertificate() + "' ，过期时间：" + lockMo.getExpireDate());
                sNum++;
            } else {
                System.out.println(" '" + lockMo.getLockName() + "' 获取锁失败，锁的过期时间：" + lockMo.getExpireDate());
            }

            // 第二次成功获取锁时，停止！
            if (sNum == 2) {
                break;
            }

            Thread.sleep(1000);
        }
    }


}
